Setup
Welcome to another tutorial for this class, COMP/STAT 112: Introduction to Data Science! It will be similar to the others, including demo videos and files embedded in this document and practice problems with hints or solutions at the end. There are some new libraries, so be sure to install those first.
As most of our files do, we start this one with three R code chunks: 1. options, 2. libraries and settings, 3. data.
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)
library(tidyverse) # for data cleaning and plotting
library(googlesheets4) # for reading googlesheet data
library(lubridate) # for date manipulation
library(openintro) # for the abbr2state() function
library(palmerpenguins)# for Palmer penguin data
library(maps) # for map data
library(ggmap) # for mapping points on maps
library(gplots) # for col2hex() function
library(RColorBrewer) # for color palettes
library(sf) # for working with spatial data
library(leaflet) # for highly customizable mapping
library(ggthemes) # for more themes (including theme_map())
gs4_deauth() # To not have to authorize each time you knit.
theme_set(theme_minimal())
# Starbucks locations
Starbucks <- read_csv("https://www.macalester.edu/~ajohns24/Data/Starbucks.csv")
# Lisa's favorite St. Paul places - used in leaflet example
favorite_stp_by_lisa <- tibble(
place = c("Home", "Macalester College", "Adams Spanish Immersion",
"Spirit Gymnastics", "Bama & Bapa", "Now Bikes",
"Dance Spectrum", "Pizza Luce", "Brunson's"),
long = c(-93.1405743, -93.1712321, -93.1451796,
-93.1650563, -93.1542883, -93.1696608,
-93.1393172, -93.1524256, -93.0753863),
lat = c(44.950576, 44.9378965, 44.9237914,
44.9654609, 44.9295072, 44.9436813,
44.9399922, 44.9468848, 44.9700727)
)
# North Carolina births and sids deaths
nc <- st_read(system.file("shape/nc.shp", package="sf"),
quiet = TRUE) %>%
mutate(sid_per_1000birth_79 = SID79/BIR79*1000)
# Compute centroid (center) of each county
county_centroid_lat_long <- as.tibble(st_coordinates(st_centroid(nc))) %>%
rename(centr_long = X,
centr_lat = Y)
# County names and sid_per_1000birth_79
nc_centroid_county <- st_drop_geometry(nc) %>%
select(NAME, sid_per_1000birth_79)
# All centroid level info
nc_centroid <- county_centroid_lat_long %>%
bind_cols(nc_centroid_county)
Plotting points on a map
The Starbucks data, compiled by Danny Kaplan and provided by Alicia Johnson, contains information about every Starbucks in the world at the time the data were collected. It includes the Latitude and Longitude of each location. Let’s start by using familiar plotting tools
ggplot(data=Starbucks) +
geom_point(aes(x = Longitude, y = Latitude),
alpha = 0.2,
size = .1)

The point pattern probably looks familiar. To highlight the geographical nature of this scatterplot, we can superimpose the points on top of a map, using the ggmap() function from the ggmap library.
NOTE: we used to be able to easily bring in Google maps. As of mid-2018, in order to bring those in, you need to have a registered API key. If you want to do that, see google_key in the help. Then, see the documentation for get_map(). We will bring in other types of maps since Google maps are harder to do now and require you to submit credit card information.
Instead, we bring in a stamen map (there are others you could try, but we’ll stick with this). You can also take a look at stamen maps on their website. First, let’s look at an example.
# Get the map information
world <- get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
# Plot the points on the map
ggmap(world) + # creates the map "background"
geom_point(data = Starbucks,
aes(x = Longitude, y = Latitude),
alpha = .3,
size = .1) +
theme_map()

Next, we will walk through the get_stamenmap() function arguments. The code below is what was used to get the world map information.
get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
bbox
get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
The bbox argument tells it the minimum and maximum latitude and longitude points. So, left is the minimum longitude, right is the maximum longitude, bottom is the minimum latitude, and top is the maximum latitude. I found it helpful to go to openstreetmap: zoom in on the area of interest, click export, and you will see all the values you need. I had to modify them slightly, which you can do after your initial plot.
maptype
get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
The maptype tells it the style of the map. Check out the different options by looking in the get_stamenmap help (type ?get_stamenmap in the console).
zoom
get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
When you make a large area, you need to decrease the zoom, otherwise it will take too long to load. So, it’s a good idea to start with a small zoom and you can always make it bigger if you want. This might seem counter-intuitive at first. I think of the zoom level as the level of detail. So, smaller numbers show less detail and larger numbers more detail. I often go to the stamanmaps webpage and search for the location I’m mapping. Then, in the URL, you can see the zoom number. For example, this link is a map of St. Paul: http://maps.stamen.com/#terrain/12/44.9531/-93.0904. Notice the number 12 next to /#terrain/. That means it is zoomed in at 12.
ggmap()
We save the the map information from get_stamenmap() to a named value and then use it in ggmap():
# Get the map information
world <- get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
# Plot the points on the map
ggmap(world) + # creates the map "background"
geom_point(data = Starbucks,
aes(x = Longitude, y = Latitude),
alpha = .3,
size = .1) +
theme_map()
The ggmap() function will print the “background” map. Think of it as the providing the canvas on which we will plot. This takes the place of our usual ggplot().
ggmap(world)

After that, we can use the geom_XXX() functions from ggplot2 that we are used to in order to put points, lines, etc. on top of the map. But, we need to remember to also provide the data we are using in the geom_XXX() function(s) we use since we do not have the ggplot() function in which to provide it.
# Get the map information
world <- get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
# Plot the points on the map
ggmap(world) + # creates the map "background"
geom_point(data = Starbucks,
aes(x = Longitude, y = Latitude),
alpha = .3,
size = .1) +
theme_map()
theme_map()
The last thing I did in the code was to add theme_map(). This is optional, but I often find it makes it look nice.
# Get the map information
world <- get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
# Plot the points on the map
ggmap(world) + # creates the map "background"
geom_point(data = Starbucks,
aes(x = Longitude, y = Latitude),
alpha = .3,
size = .1) +
theme_map()

So, the final map as a world map as the background with points plotted on top that show the Starbucks locations. The points are .1 of their usual size and have a transparency level of .3.
Your turn!
Exercise: More with Starbucks
Add an aesthetic to the world map that sets the color of the points according to the ownership type. What, if anything, can you deduce from this visualization?
Construct a new map of Starbucks locations in the Twin Cities metro area (approximately the 5 county metro area).
In the Twin Cities plot, play with the zoom number. What does it do? (just describe what it does - don’t actually include more than one map).
Try a couple different map types (see get_stamenmap() in help and look at maptype). Include a map with one of the other map types.
Add a point to the map that indicates Macalester College and label it appropriately. There are many ways you can do think, but I think it’s easiest with the annotate() function (see ggplot2 cheatsheet).
Choropleths
Geographical data needn’t be expressed by latitude and longitude. For choropleth maps, instead of visualizing our data as points with different aesthetics (size, color, transparency, etc.), we color different regions of the maps based on data values. To do this we need to specify both the geometric regions on which the data resides (counties, states, zip codes, etc.), and then wrangle the data so that there is one value per region.
Let’s return to the Starbucks data. First, we will create a new dataset, starbucks_us_by_state that limits the data to the US, finds the number of Starbucks in each state, and creates a state name that is in all lowercase letters that matches the state name in the region variable of the states_map dataset.
The states_map dataset gives information about creating the borders of the US states. The data is retrieved using the map_data() function. Run ?map_data in the console to see more information about what other maps are available. There are also other packages that provide different types of maps.
Then, we can use geom_map() to create a choropleth map. Let’s take a look at the map and we’ll go through the details after.
#Create a new Starbucks dataset that
# - filters to the US
# - summarizes the number of Starbucks in each state
# - has full names of states in lowercase letters (to match to states_map data created next)
starbucks_us_by_state <- Starbucks %>%
filter(Country == "US") %>%
count(`State/Province`) %>%
mutate(state_name = str_to_lower(abbr2state(`State/Province`)))
#US states map information - coordinates used to draw borders
states_map <- map_data("state")
# map that colors state by number of Starbucks
starbucks_us_by_state %>%
ggplot() +
geom_map(map = states_map,
aes(map_id = state_name,
fill = n)) +
#This assures the map looks decently nice:
expand_limits(x = states_map$long, y = states_map$lat) +
theme_map()

Now, let’s look more closely at what each piece of the code below is doing.
starbucks_us_by_state %>%
ggplot() +
geom_map(map = states_map,
aes(map_id = state_name,
fill = n)) +
expand_limits(x = states_map$long, y = states_map$lat) +
theme_map()
Choose a map
The map argument tells R at which level to create the map. Really, it tells it how to draw all the borders This is a very special data set. According to the geom_map() documentation, it is a “data frame that contains the map coordinates … It must contain columns x or long, y or lat, and region or id.” We are using the map_data() function to create the map file (see above for more detail0. You can open the map data, states_map, and see that it adheres to the rules.
starbucks_us_by_state %>%
ggplot() +
geom_map(map = states_map,
aes(map_id = state_name,
fill = n)) +
expand_limits(x = states_map$long, y = states_map$lat) +
theme_map()
Connect map id/region variable to data being plotted
The map_id inside of aes() is a required aesthetic for the geom_map() geom. It tells R which variable is the region/id variable, in this case the state. It connects the region or id from the map (region variable in states_map dataset, in this example) to the dataset being plotted (state_name in starbucks_us_by_state, in this example). So state_name needs to have the same form as region, which is why we modified the state names in starbucks_us_by_state.
starbucks_us_by_state %>%
ggplot() +
geom_map(map = states_map,
aes(map_id = state_name,
fill = n)) +
expand_limits(x = states_map$long, y = states_map$lat) +
theme_map()
Use ggplot2 features
We tell it to fill in the states by the variable n, the number of Starbucks in each state. With the geom_map() geom, it will fill in the borders of the regions we defined in the map argument.
starbucks_us_by_state %>%
ggplot() +
geom_map(map = states_map,
aes(map_id = state_name,
fill = n)) +
expand_limits(x = states_map$long, y = states_map$lat) +
theme_map()
expand_limits()
Use expand_limits() to assure that the map covers the entire area it’s supposed to. We put the longitude variable from states_map for the x argument and the latitude variable from states_map for the y argument to assure the map stretches across the entire range of longitudes and latitudes in the map. There may be a better way to do this but I have yet to find it, and when I leave it out, I don’t even see the map.
starbucks_us_by_state %>%
ggplot() +
geom_map(map = states_map,
aes(map_id = state_name,
fill = n)) +
expand_limits(x = states_map$long, y = states_map$lat) +
theme_map()
theme_map()
This is a personal preference. I like the way theme_map() makes the map look.
starbucks_us_by_state %>%
ggplot() +
geom_map(map = states_map,
aes(map_id = state_name,
fill = n)) +
expand_limits(x = states_map$long, y = states_map$lat) +
theme_map()
Add ggplot2 layers
You can add any of the ggplot2 layers on top of this map. In this example, I’ve added the MN Starbucks as points, a title, and changed the legend background (so it doesn’t have one and overlap California).
starbucks_us_by_state %>%
ggplot() +
geom_map(map = states_map,
aes(map_id = state_name,
fill = n)) +
geom_point(data = Starbucks %>% filter(`State/Province` == "MN"),
aes(x = Longitude, y = Latitude),
size = .05,
alpha = .2,
color = "goldenrod") +
expand_limits(x = states_map$long, y = states_map$lat) +
labs(title = "Starbucks in MN") +
theme_map() +
theme(legend.background = element_blank())

Your turn!
Exercise: Even more with Starbucks
The example I showed did not account for population of each state in the map. In the code below, a new variable is created, starbucks_per_10000, that gives the number of Starbucks per 10,000 people. It is in the starbucks_with_2018_pop_est dataset.
census_pop_est_2018 <- read_csv("https://www.dropbox.com/s/6txwv3b4ng7pepe/us_census_2018_state_pop_est.csv?dl=1") %>%
separate(state, into = c("dot","state"), extra = "merge") %>%
select(-dot) %>%
mutate(state = str_to_lower(state))
starbucks_with_2018_pop_est <-
starbucks_us_by_state %>%
left_join(census_pop_est_2018,
by = c("state_name" = "state")) %>%
mutate(starbucks_per_10000 = (n/est_pop_2018)*10000)
dplyr review: Look through the code above and describe what each line of code does.
Create a choropleth map that shows the number of Starbucks per 10,000 people on a map of the US. Use a new fill color, add points for all Starbucks in the US (except Hawaii and Alaska), add an informative title for the plot, and include a caption that says who created the plot (you!). Make a conclusion about what you observe.
LS0tCnRpdGxlOiAiTWFwcGluZyBkYXRhIGluIFIiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgojIyBTZXR1cAoKV2VsY29tZSB0byBhbm90aGVyIHR1dG9yaWFsIGZvciB0aGlzIGNsYXNzLCBDT01QL1NUQVQgMTEyOiAqSW50cm9kdWN0aW9uIHRvIERhdGEgU2NpZW5jZSohIEl0IHdpbGwgYmUgc2ltaWxhciB0byB0aGUgb3RoZXJzLCBpbmNsdWRpbmcgZGVtbyB2aWRlb3MgYW5kIGZpbGVzIGVtYmVkZGVkIGluIHRoaXMgZG9jdW1lbnQgYW5kIHByYWN0aWNlIHByb2JsZW1zIHdpdGggaGludHMgb3Igc29sdXRpb25zIGF0IHRoZSBlbmQuIFRoZXJlIGFyZSBzb21lIG5ldyBsaWJyYXJpZXMsIHNvIGJlIHN1cmUgdG8gaW5zdGFsbCB0aG9zZSBmaXJzdC4KCkFzIG1vc3Qgb2Ygb3VyIGZpbGVzIGRvLCB3ZSBzdGFydCB0aGlzIG9uZSB3aXRoIHRocmVlIFIgY29kZSBjaHVua3M6IDEuIG9wdGlvbnMsIDIuIGxpYnJhcmllcyBhbmQgc2V0dGluZ3MsIDMuIGRhdGEuIAoKYGBge3Igc2V0dXB9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UpCmBgYAoKYGBge3IgbGlicmFyaWVzfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAgICMgZm9yIGRhdGEgY2xlYW5pbmcgYW5kIHBsb3R0aW5nCmxpYnJhcnkoZ29vZ2xlc2hlZXRzNCkgIyBmb3IgcmVhZGluZyBnb29nbGVzaGVldCBkYXRhCmxpYnJhcnkobHVicmlkYXRlKSAgICAgIyBmb3IgZGF0ZSBtYW5pcHVsYXRpb24KbGlicmFyeShvcGVuaW50cm8pICAgICAjIGZvciB0aGUgYWJicjJzdGF0ZSgpIGZ1bmN0aW9uCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpIyBmb3IgUGFsbWVyIHBlbmd1aW4gZGF0YQpsaWJyYXJ5KG1hcHMpICAgICAgICAgICMgZm9yIG1hcCBkYXRhCmxpYnJhcnkoZ2dtYXApICAgICAgICAgIyBmb3IgbWFwcGluZyBwb2ludHMgb24gbWFwcwpsaWJyYXJ5KGdwbG90cykgICAgICAgICMgZm9yIGNvbDJoZXgoKSBmdW5jdGlvbgpsaWJyYXJ5KFJDb2xvckJyZXdlcikgICMgZm9yIGNvbG9yIHBhbGV0dGVzCmxpYnJhcnkoc2YpICAgICAgICAgICAgIyBmb3Igd29ya2luZyB3aXRoIHNwYXRpYWwgZGF0YQpsaWJyYXJ5KGxlYWZsZXQpICAgICAgICMgZm9yIGhpZ2hseSBjdXN0b21pemFibGUgbWFwcGluZwpsaWJyYXJ5KGdndGhlbWVzKSAgICAgICMgZm9yIG1vcmUgdGhlbWVzIChpbmNsdWRpbmcgdGhlbWVfbWFwKCkpCmdzNF9kZWF1dGgoKSAgICAgICAgICAgIyBUbyBub3QgaGF2ZSB0byBhdXRob3JpemUgZWFjaCB0aW1lIHlvdSBrbml0Lgp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKQpgYGAKCmBgYHtyIG15X2xpYnJhcmllcywgaW5jbHVkZT1GQUxTRX0KIyBMaXNhIG5lZWRzIHRoaXMsIHN0dWRlbnRzIGRvbid0CmxpYnJhcnkoZG93bmxvYWR0aGlzKSAjIGZvciBpbmNsdWRpbmcgZG93bmxvYWQgYnV0dG9ucyBmb3IgZmlsZXMKbGlicmFyeShmbGFpcikgIyBmb3IgaGlnaGxpZ2h0aW5nIGNvZGUKYGBgCgpgYGB7ciBkYXRhfQojIFN0YXJidWNrcyBsb2NhdGlvbnMKU3RhcmJ1Y2tzIDwtIHJlYWRfY3N2KCJodHRwczovL3d3dy5tYWNhbGVzdGVyLmVkdS9+YWpvaG5zMjQvRGF0YS9TdGFyYnVja3MuY3N2IikKCiMgTGlzYSdzIGZhdm9yaXRlIFN0LiBQYXVsIHBsYWNlcyAtIHVzZWQgaW4gbGVhZmxldCBleGFtcGxlCmZhdm9yaXRlX3N0cF9ieV9saXNhIDwtIHRpYmJsZSgKICBwbGFjZSA9IGMoIkhvbWUiLCAiTWFjYWxlc3RlciBDb2xsZWdlIiwgIkFkYW1zIFNwYW5pc2ggSW1tZXJzaW9uIiwgCiAgICAgICAgICAgICJTcGlyaXQgR3ltbmFzdGljcyIsICJCYW1hICYgQmFwYSIsICJOb3cgQmlrZXMiLAogICAgICAgICAgICAiRGFuY2UgU3BlY3RydW0iLCAiUGl6emEgTHVjZSIsICJCcnVuc29uJ3MiKSwKICBsb25nID0gYygtOTMuMTQwNTc0MywgLTkzLjE3MTIzMjEsIC05My4xNDUxNzk2LCAKICAgICAgICAgICAtOTMuMTY1MDU2MywgLTkzLjE1NDI4ODMsIC05My4xNjk2NjA4LCAKICAgICAgICAgICAtOTMuMTM5MzE3MiwgLTkzLjE1MjQyNTYsIC05My4wNzUzODYzKSwKICBsYXQgPSBjKDQ0Ljk1MDU3NiwgNDQuOTM3ODk2NSwgNDQuOTIzNzkxNCwKICAgICAgICAgIDQ0Ljk2NTQ2MDksIDQ0LjkyOTUwNzIsIDQ0Ljk0MzY4MTMsIAogICAgICAgICAgNDQuOTM5OTkyMiwgNDQuOTQ2ODg0OCwgNDQuOTcwMDcyNykKICApCgoKIyBOb3J0aCBDYXJvbGluYSBiaXJ0aHMgYW5kIHNpZHMgZGVhdGhzIAoKbmMgPC0gc3RfcmVhZChzeXN0ZW0uZmlsZSgic2hhcGUvbmMuc2hwIiwgcGFja2FnZT0ic2YiKSwgCiAgICAgICAgICAgICAgcXVpZXQgPSBUUlVFKSAlPiUgCiAgbXV0YXRlKHNpZF9wZXJfMTAwMGJpcnRoXzc5ID0gU0lENzkvQklSNzkqMTAwMCkKCiMgQ29tcHV0ZSBjZW50cm9pZCAoY2VudGVyKSBvZiBlYWNoIGNvdW50eQpjb3VudHlfY2VudHJvaWRfbGF0X2xvbmcgPC0gYXMudGliYmxlKHN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKG5jKSkpICU+JSAKICByZW5hbWUoY2VudHJfbG9uZyA9IFgsIAogICAgICAgICBjZW50cl9sYXQgPSBZKQoKIyBDb3VudHkgbmFtZXMgYW5kIHNpZF9wZXJfMTAwMGJpcnRoXzc5Cm5jX2NlbnRyb2lkX2NvdW50eSA8LSBzdF9kcm9wX2dlb21ldHJ5KG5jKSAlPiUgCiAgc2VsZWN0KE5BTUUsIHNpZF9wZXJfMTAwMGJpcnRoXzc5KQoKIyBBbGwgY2VudHJvaWQgbGV2ZWwgaW5mbwpuY19jZW50cm9pZCA8LSAgY291bnR5X2NlbnRyb2lkX2xhdF9sb25nICU+JSAKICBiaW5kX2NvbHMobmNfY2VudHJvaWRfY291bnR5KQpgYGAKCiMjIExlYXJuaW5nIEdvYWxzCgpBZnRlciB0aGlzIHR1dG9yaWFsLCB5b3Ugc2hvdWxkIGJlIGFibGUgdG8gZG8gdGhlIGZvbGxvd2luZzoKCiogUGxvdCBkYXRhIHBvaW50cyBvbiB0b3Agb2YgYSBtYXAgdXNpbmcgdGhlIGBnZ21hcCgpYCBmdW5jdGlvbiBhbG9uZyB3aXRoIGBnZ3Bsb3QyYCBmdW5jdGlvbnMuICAKCiogQ3JlYXRlIGNob3JvcGxldGggbWFwcyB1c2luZyBgZ2VvbV9tYXAoKWAuICAKCiogQWRkIHBvaW50cyBhbmQgb3RoZXIgYGdncGxvdDJgIGZlYXR1cmVzIHRvIGEgbWFwIGNyZWF0ZWQgZnJvbSBgZ2VvbV9tYXAoKWAuICAKCiogVW5kZXJzdGFuZCB0aGUgYmFzaWNzIG9mIGNyZWF0aW5nIGEgbWFwIHVzaW5nIGBsZWFmbGV0YCwgaW5jbHVkaW5nIGFkZGluZyBwb2ludHMgYW5kIGNob3JvcGxldGhzIHRvIGEgYmFzZSBtYXAuCgoKIyMgUGxvdHRpbmcgcG9pbnRzIG9uIGEgbWFwCgpUaGUgYFN0YXJidWNrc2AgZGF0YSwgY29tcGlsZWQgYnkgRGFubnkgS2FwbGFuIGFuZCBwcm92aWRlZCBieSBBbGljaWEgSm9obnNvbiwgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgZXZlcnkgU3RhcmJ1Y2tzIGluIHRoZSB3b3JsZCBhdCB0aGUgdGltZSB0aGUgZGF0YSB3ZXJlIGNvbGxlY3RlZC4gSXQgaW5jbHVkZXMgdGhlIGBMYXRpdHVkZWAgYW5kIGBMb25naXR1ZGVgIG9mIGVhY2ggbG9jYXRpb24uICBMZXQncyBzdGFydCBieSB1c2luZyBmYW1pbGlhciBwbG90dGluZyB0b29scwoKYGBge3J9CmdncGxvdChkYXRhPVN0YXJidWNrcykgKwogIGdlb21fcG9pbnQoYWVzKHggPSBMb25naXR1ZGUsIHkgPSBMYXRpdHVkZSksIAogICAgICAgICAgICAgYWxwaGEgPSAwLjIsIAogICAgICAgICAgICAgc2l6ZSA9IC4xKQpgYGAKClRoZSBwb2ludCBwYXR0ZXJuIHByb2JhYmx5IGxvb2tzIGZhbWlsaWFyLiAgVG8gaGlnaGxpZ2h0IHRoZSBnZW9ncmFwaGljYWwgbmF0dXJlIG9mIHRoaXMgc2NhdHRlcnBsb3QsIHdlIGNhbiBzdXBlcmltcG9zZSB0aGUgcG9pbnRzIG9uIHRvcCBvZiBhIG1hcCwgdXNpbmcgdGhlIGBnZ21hcCgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZ2dtYXBgIGxpYnJhcnkuIAoKKipOT1RFKio6IHdlIHVzZWQgdG8gYmUgYWJsZSB0byAqZWFzaWx5KiBicmluZyBpbiBHb29nbGUgbWFwcy4gQXMgb2YgbWlkLTIwMTgsIGluIG9yZGVyIHRvIGJyaW5nIHRob3NlIGluLCB5b3UgbmVlZCB0byBoYXZlIGEgcmVnaXN0ZXJlZCBBUEkga2V5LiBJZiB5b3Ugd2FudCB0byBkbyB0aGF0LCBzZWUgYGdvb2dsZV9rZXlgIGluIHRoZSBoZWxwLiBUaGVuLCBzZWUgdGhlIGRvY3VtZW50YXRpb24gZm9yIGBnZXRfbWFwKClgLiBXZSB3aWxsIGJyaW5nIGluIG90aGVyIHR5cGVzIG9mIG1hcHMgc2luY2UgR29vZ2xlIG1hcHMgYXJlIGhhcmRlciB0byBkbyBub3cgYW5kIHJlcXVpcmUgeW91IHRvIHN1Ym1pdCBjcmVkaXQgY2FyZCBpbmZvcm1hdGlvbi4KCkluc3RlYWQsIHdlIGJyaW5nIGluIGEgc3RhbWVuIG1hcCAodGhlcmUgYXJlIG90aGVycyB5b3UgY291bGQgdHJ5LCBidXQgd2UnbGwgc3RpY2sgd2l0aCB0aGlzKS4gWW91IGNhbiBhbHNvIHRha2UgYSBsb29rIGF0IHN0YW1lbiBtYXBzIG9uIHRoZWlyIFt3ZWJzaXRlXShodHRwOi8vbWFwcy5zdGFtZW4uY29tLyN3YXRlcmNvbG9yLzEyLzM3Ljc3MDYvLTEyMi4zNzgyKS4gRmlyc3QsIGxldCdzIGxvb2sgYXQgYW4gZXhhbXBsZS4gCgpgYGB7ciBzdGFyYnVja3MtbWFwfQojIEdldCB0aGUgbWFwIGluZm9ybWF0aW9uCndvcmxkIDwtIGdldF9zdGFtZW5tYXAoCiAgICBiYm94ID0gYyhsZWZ0ID0gLTE4MCwgYm90dG9tID0gLTU3LCByaWdodCA9IDE3OSwgdG9wID0gODIuMSksIAogICAgbWFwdHlwZSA9ICJ0ZXJyYWluIiwKICAgIHpvb20gPSAyKQoKIyBQbG90IHRoZSBwb2ludHMgb24gdGhlIG1hcApnZ21hcCh3b3JsZCkgKyAjIGNyZWF0ZXMgdGhlIG1hcCAiYmFja2dyb3VuZCIKICBnZW9tX3BvaW50KGRhdGEgPSBTdGFyYnVja3MsIAogICAgICAgICAgICAgYWVzKHggPSBMb25naXR1ZGUsIHkgPSBMYXRpdHVkZSksIAogICAgICAgICAgICAgYWxwaGEgPSAuMywgCiAgICAgICAgICAgICBzaXplID0gLjEpICsKICB0aGVtZV9tYXAoKQpgYGAKCk5leHQsIHdlIHdpbGwgd2FsayB0aHJvdWdoIHRoZSBgZ2V0X3N0YW1lbm1hcCgpYCBmdW5jdGlvbiBhcmd1bWVudHMuIFRoZSBjb2RlIGJlbG93IGlzIHdoYXQgd2FzIHVzZWQgdG8gZ2V0IHRoZSB3b3JsZCBtYXAgaW5mb3JtYXRpb24uCgpgYGB7ciBnZXRfc3RhbWVubWFwLWNvZGUsIGV2YWw9RkFMU0V9CmdldF9zdGFtZW5tYXAoCiAgICBiYm94ID0gYyhsZWZ0ID0gLTE4MCwgYm90dG9tID0gLTU3LCByaWdodCA9IDE3OSwgdG9wID0gODIuMSksIAogICAgbWFwdHlwZSA9ICJ0ZXJyYWluIiwKICAgIHpvb20gPSAyKQpgYGAKCioqYGJib3hgKioKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygiZ2V0X3N0YW1lbm1hcC1jb2RlIiwgZXZhbCA9IEZBTFNFKSAlPiUgCiAgZmxhaXIoImJib3ggPSAiKQpgYGAKCgpUaGUgYGJib3hgIGFyZ3VtZW50IHRlbGxzIGl0IHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgcG9pbnRzLiBTbywgbGVmdCBpcyB0aGUgbWluaW11bSBsb25naXR1ZGUsIHJpZ2h0IGlzIHRoZSBtYXhpbXVtIGxvbmdpdHVkZSwgYm90dG9tIGlzIHRoZSBtaW5pbXVtIGxhdGl0dWRlLCBhbmQgdG9wIGlzIHRoZSBtYXhpbXVtIGxhdGl0dWRlLiBJIGZvdW5kIGl0IGhlbHBmdWwgdG8gZ28gdG8gW29wZW5zdHJlZXRtYXBdKGh0dHBzOi8vd3d3Lm9wZW5zdHJlZXRtYXAub3JnKTogem9vbSBpbiBvbiB0aGUgYXJlYSBvZiBpbnRlcmVzdCwgY2xpY2sgZXhwb3J0LCBhbmQgeW91IHdpbGwgc2VlIGFsbCB0aGUgdmFsdWVzIHlvdSBuZWVkLiBJIGhhZCB0byBtb2RpZnkgdGhlbSBzbGlnaHRseSwgd2hpY2ggeW91IGNhbiBkbyBhZnRlciB5b3VyIGluaXRpYWwgcGxvdC4KCioqYG1hcHR5cGVgKioKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygiZ2V0X3N0YW1lbm1hcC1jb2RlIiwgZXZhbCA9IEZBTFNFKSAlPiUgCiAgZmxhaXIoIm1hcHR5cGUgPSAiKQpgYGAKClRoZSBgbWFwdHlwZWAgdGVsbHMgaXQgdGhlIHN0eWxlIG9mIHRoZSBtYXAuIENoZWNrIG91dCB0aGUgZGlmZmVyZW50IG9wdGlvbnMgYnkgbG9va2luZyBpbiB0aGUgYGdldF9zdGFtZW5tYXBgIGhlbHAgKHR5cGUgYD9nZXRfc3RhbWVubWFwYCBpbiB0aGUgY29uc29sZSkuCgoqKmB6b29tYCoqCgpgYGB7ciwgZWNobz1GQUxTRX0KZGVjb3JhdGVfY2h1bmsoImdldF9zdGFtZW5tYXAtY29kZSIsIGV2YWwgPSBGQUxTRSkgJT4lIAogIGZsYWlyKCJ6b29tID0gIikKYGBgCgpXaGVuIHlvdSBtYWtlIGEgbGFyZ2UgYXJlYSwgeW91IG5lZWQgdG8gZGVjcmVhc2UgdGhlIHpvb20sIG90aGVyd2lzZSBpdCB3aWxsIHRha2UgdG9vIGxvbmcgdG8gbG9hZC4gU28sIGl0J3MgYSBnb29kIGlkZWEgdG8gc3RhcnQgd2l0aCBhIHNtYWxsIHpvb20gYW5kIHlvdSBjYW4gYWx3YXlzIG1ha2UgaXQgYmlnZ2VyIGlmIHlvdSB3YW50LiBUaGlzIG1pZ2h0IHNlZW0gY291bnRlci1pbnR1aXRpdmUgYXQgZmlyc3QuIEkgdGhpbmsgb2YgdGhlIHpvb20gbGV2ZWwgYXMgdGhlIGxldmVsIG9mIGRldGFpbC4gU28sIHNtYWxsZXIgbnVtYmVycyBzaG93IGxlc3MgZGV0YWlsIGFuZCBsYXJnZXIgbnVtYmVycyBtb3JlIGRldGFpbC4gSSBvZnRlbiBnbyB0byB0aGUgc3RhbWFubWFwcyB3ZWJwYWdlIGFuZCBzZWFyY2ggZm9yIHRoZSBsb2NhdGlvbiBJJ20gbWFwcGluZy4gVGhlbiwgaW4gdGhlIFVSTCwgeW91IGNhbiBzZWUgdGhlIHpvb20gbnVtYmVyLiBGb3IgZXhhbXBsZSwgdGhpcyBsaW5rICBpcyBhIG1hcCBvZiBTdC4gUGF1bDogW2h0dHA6Ly9tYXBzLnN0YW1lbi5jb20vI3RlcnJhaW4vMTIvNDQuOTUzMS8tOTMuMDkwNF0oaHR0cDovL21hcHMuc3RhbWVuLmNvbS8jdGVycmFpbi8xMi80NC45NTMxLy05My4wOTA0KS4gTm90aWNlIHRoZSBudW1iZXIgYDEyYCBuZXh0IHRvICBgLyN0ZXJyYWluL2AuIFRoYXQgbWVhbnMgaXQgaXMgem9vbWVkIGluIGF0IDEyLiAKCioqYGdnbWFwKClgKioKCldlIHNhdmUgdGhlIHRoZSBtYXAgaW5mb3JtYXRpb24gZnJvbSBgZ2V0X3N0YW1lbm1hcCgpYCB0byBhIG5hbWVkIHZhbHVlIGFuZCB0aGVuIHVzZSBpdCBpbiBgZ2dtYXAoKWA6CgpgYGB7ciwgZWNobz1GQUxTRX0KZGVjb3JhdGVfY2h1bmsoInN0YXJidWNrcy1tYXAiLCBldmFsID0gRkFMU0UpICU+JSAKICBmbGFpcigid29ybGQgPC0iKSAlPiUgCiAgZmxhaXIoIndvcmxkIikKYGBgCgpUaGUgYGdnbWFwKClgIGZ1bmN0aW9uIHdpbGwgcHJpbnQgdGhlICJiYWNrZ3JvdW5kIiBtYXAuIFRoaW5rIG9mIGl0IGFzIHRoZSBwcm92aWRpbmcgdGhlIGNhbnZhcyBvbiB3aGljaCB3ZSB3aWxsIHBsb3QuIFRoaXMgdGFrZXMgdGhlIHBsYWNlIG9mIG91ciB1c3VhbCBgZ2dwbG90KClgLgoKYGBge3J9CmdnbWFwKHdvcmxkKQpgYGAKCkFmdGVyIHRoYXQsIHdlIGNhbiB1c2UgdGhlIGBnZW9tX1hYWCgpYCBmdW5jdGlvbnMgZnJvbSBgZ2dwbG90MmAgdGhhdCB3ZSBhcmUgdXNlZCB0byBpbiBvcmRlciB0byBwdXQgcG9pbnRzLCBsaW5lcywgZXRjLiBvbiB0b3Agb2YgdGhlIG1hcC4gQnV0LCB3ZSBuZWVkIHRvIHJlbWVtYmVyIHRvIGFsc28gcHJvdmlkZSB0aGUgZGF0YSB3ZSBhcmUgdXNpbmcgaW4gdGhlIGBnZW9tX1hYWCgpYCBmdW5jdGlvbihzKSB3ZSB1c2Ugc2luY2Ugd2UgZG8gbm90IGhhdmUgdGhlIGBnZ3Bsb3QoKWAgZnVuY3Rpb24gaW4gd2hpY2ggdG8gcHJvdmlkZSBpdC4gCgpgYGB7ciwgZWNobz1GQUxTRX0KZGVjb3JhdGVfY2h1bmsoInN0YXJidWNrcy1tYXAiLCBldmFsID0gRkFMU0UpICU+JSAKICBmbGFpcigiZGF0YSA9IFN0YXJidWNrcyIpCmBgYAoKCioqYHRoZW1lX21hcCgpYCoqCgpUaGUgbGFzdCB0aGluZyBJIGRpZCBpbiB0aGUgY29kZSB3YXMgdG8gYWRkIGB0aGVtZV9tYXAoKWAuIFRoaXMgaXMgb3B0aW9uYWwsIGJ1dCBJIG9mdGVuIGZpbmQgaXQgbWFrZXMgaXQgbG9vayBuaWNlLgoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJzdGFyYnVja3MtbWFwIikgJT4lIAogIGZsYWlyKCJ0aGVtZV9tYXAoKSIpCmBgYAoKU28sIHRoZSBmaW5hbCBtYXAgYXMgYSB3b3JsZCBtYXAgYXMgdGhlIGJhY2tncm91bmQgd2l0aCBwb2ludHMgcGxvdHRlZCBvbiB0b3AgdGhhdCBzaG93IHRoZSBTdGFyYnVja3MgbG9jYXRpb25zLiBUaGUgcG9pbnRzIGFyZSAuMSBvZiB0aGVpciB1c3VhbCBzaXplIGFuZCBoYXZlIGEgdHJhbnNwYXJlbmN5IGxldmVsIG9mIC4zLiAKCgojIyMgRGVtbyB2aWRlbwoKV2l0aCB0aGF0IGludHJvZHVjdGlvbiwgeW91IGFyZSByZWFkeSB0byB3YXRjaCB0aGUgZGVtbyB2aWRlbyEKCjxpZnJhbWUgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvMms4Ty1ZX3VpUlUiIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYWNjZWxlcm9tZXRlcjsgYXV0b3BsYXk7IGNsaXBib2FyZC13cml0ZTsgZW5jcnlwdGVkLW1lZGlhOyBneXJvc2NvcGU7IHBpY3R1cmUtaW4tcGljdHVyZSIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPgoKW1ZvaWNldGhyZWFkOiBgZ2dtYXBgIGRlbW9dKGh0dHBzOi8vdm9pY2V0aHJlYWQuY29tL3NoYXJlLzE1NDc5Njg4LykKCmBgYHtyLCBlY2hvPUZBTFNFfQpkb3dubG9hZF9maWxlKAogIHBhdGggPSAiMDRfZ2dtYXBfZGVtb19ub19jb2RlLlJtZCIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGdnbWFwIGRlbW8gZmlsZSAod2l0aG91dCBjb2RlKSIsCiAgYnV0dG9uX3R5cGUgPSAid2FybmluZyIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmRvd25sb2FkX2ZpbGUoCiAgcGF0aCA9ICIwNF9nZ21hcF9kZW1vLlJtZCIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGdnbWFwIGRlbW8gZmlsZSAod2l0aCBjb2RlKSIsCiAgYnV0dG9uX3R5cGUgPSAiaW5mbyIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYAoKCiMjIyBSZXNvdXJjZXMKCiogW0V4YW1wbGVzXShodHRwczovL2dpdGh1Yi5jb20vZGthaGxlL2dnbWFwKSBmcm9tIGBnZ21hcGAgbWFpbnRhaW5lciBEYXZpZCBLYWhsZSAgCiogW2BnZ21hcGAgY2hlYXRzaGVldF0oaHR0cHM6Ly93d3cubmNlYXMudWNzYi5lZHUvc2l0ZXMvZGVmYXVsdC9maWxlcy8yMDIwLTA0L2dnbWFwQ2hlYXRzaGVldC5wZGYpCgojIyMgWW91ciB0dXJuIQoKIyMjIyBFeGVyY2lzZTogTW9yZSB3aXRoIFN0YXJidWNrcwoKYS4gQWRkIGFuIGFlc3RoZXRpYyB0byB0aGUgd29ybGQgbWFwIHRoYXQgc2V0cyB0aGUgY29sb3Igb2YgdGhlIHBvaW50cyBhY2NvcmRpbmcgdG8gdGhlIG93bmVyc2hpcCB0eXBlLiBXaGF0LCBpZiBhbnl0aGluZywgY2FuIHlvdSBkZWR1Y2UgZnJvbSB0aGlzIHZpc3VhbGl6YXRpb24/ICAKCmIuIENvbnN0cnVjdCBhIG5ldyBtYXAgb2YgU3RhcmJ1Y2tzIGxvY2F0aW9ucyBpbiB0aGUgVHdpbiBDaXRpZXMgbWV0cm8gYXJlYSAoYXBwcm94aW1hdGVseSB0aGUgNSBjb3VudHkgbWV0cm8gYXJlYSkuICAKCmMuIEluIHRoZSBUd2luIENpdGllcyBwbG90LCBwbGF5IHdpdGggdGhlIHpvb20gbnVtYmVyLiBXaGF0IGRvZXMgaXQgZG8/ICAoanVzdCBkZXNjcmliZSB3aGF0IGl0IGRvZXMgLSBkb24ndCBhY3R1YWxseSBpbmNsdWRlIG1vcmUgdGhhbiBvbmUgbWFwKS4gIAoKZC4gVHJ5IGEgY291cGxlIGRpZmZlcmVudCBtYXAgdHlwZXMgKHNlZSBgZ2V0X3N0YW1lbm1hcCgpYCBpbiBoZWxwIGFuZCBsb29rIGF0IGBtYXB0eXBlYCkuIEluY2x1ZGUgYSBtYXAgd2l0aCBvbmUgb2YgdGhlIG90aGVyIG1hcCB0eXBlcy4gIAoKZS4gQWRkIGEgcG9pbnQgdG8gdGhlIG1hcCB0aGF0IGluZGljYXRlcyBNYWNhbGVzdGVyIENvbGxlZ2UgYW5kIGxhYmVsIGl0IGFwcHJvcHJpYXRlbHkuIFRoZXJlIGFyZSBtYW55IHdheXMgeW91IGNhbiBkbyB0aGluaywgYnV0IEkgdGhpbmsgaXQncyBlYXNpZXN0IHdpdGggdGhlIGBhbm5vdGF0ZSgpYCBmdW5jdGlvbiAoc2VlIGBnZ3Bsb3QyYCBjaGVhdHNoZWV0KS4KCiMjIENob3JvcGxldGhzCgpHZW9ncmFwaGljYWwgZGF0YSBuZWVkbid0IGJlIGV4cHJlc3NlZCBieSBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlLiBGb3IgY2hvcm9wbGV0aCBtYXBzLCBpbnN0ZWFkIG9mIHZpc3VhbGl6aW5nIG91ciBkYXRhIGFzIHBvaW50cyB3aXRoIGRpZmZlcmVudCBhZXN0aGV0aWNzIChzaXplLCBjb2xvciwgdHJhbnNwYXJlbmN5LCBldGMuKSwgd2UgY29sb3IgZGlmZmVyZW50IHJlZ2lvbnMgb2YgdGhlIG1hcHMgYmFzZWQgb24gZGF0YSB2YWx1ZXMuIFRvIGRvIHRoaXMgd2UgbmVlZCB0byBzcGVjaWZ5IGJvdGggdGhlIGdlb21ldHJpYyByZWdpb25zIG9uIHdoaWNoIHRoZSBkYXRhIHJlc2lkZXMgKGNvdW50aWVzLCBzdGF0ZXMsIHppcCBjb2RlcywgZXRjLiksIGFuZCB0aGVuIHdyYW5nbGUgdGhlIGRhdGEgc28gdGhhdCB0aGVyZSBpcyBvbmUgdmFsdWUgcGVyIHJlZ2lvbi4gCgpMZXQncyByZXR1cm4gdG8gdGhlIFN0YXJidWNrcyBkYXRhLiBGaXJzdCwgd2Ugd2lsbCBjcmVhdGUgYSBuZXcgZGF0YXNldCwgYHN0YXJidWNrc191c19ieV9zdGF0ZWAgdGhhdCBsaW1pdHMgdGhlIGRhdGEgdG8gdGhlIFVTLCBmaW5kcyB0aGUgbnVtYmVyIG9mIFN0YXJidWNrcyBpbiBlYWNoIHN0YXRlLCBhbmQgY3JlYXRlcyBhIHN0YXRlIG5hbWUgdGhhdCBpcyBpbiBhbGwgbG93ZXJjYXNlIGxldHRlcnMgdGhhdCBtYXRjaGVzIHRoZSBzdGF0ZSBuYW1lIGluIHRoZSBgcmVnaW9uYCB2YXJpYWJsZSBvZiB0aGUgYHN0YXRlc19tYXBgIGRhdGFzZXQuCgpUaGUgYHN0YXRlc19tYXBgIGRhdGFzZXQgZ2l2ZXMgaW5mb3JtYXRpb24gYWJvdXQgY3JlYXRpbmcgdGhlIGJvcmRlcnMgb2YgdGhlIFVTIHN0YXRlcy4gVGhlIGRhdGEgaXMgcmV0cmlldmVkIHVzaW5nIHRoZSBgbWFwX2RhdGEoKWAgZnVuY3Rpb24uIFJ1biBgP21hcF9kYXRhYCBpbiB0aGUgY29uc29sZSB0byBzZWUgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB3aGF0IG90aGVyIG1hcHMgYXJlIGF2YWlsYWJsZS4gVGhlcmUgYXJlIGFsc28gb3RoZXIgcGFja2FnZXMgdGhhdCBwcm92aWRlIGRpZmZlcmVudCB0eXBlcyBvZiBtYXBzLgoKVGhlbiwgd2UgY2FuIHVzZSBgZ2VvbV9tYXAoKWAgdG8gY3JlYXRlIGEgY2hvcm9wbGV0aCBtYXAuIExldCdzIHRha2UgYSBsb29rIGF0IHRoZSBtYXAgYW5kIHdlJ2xsIGdvIHRocm91Z2ggdGhlIGRldGFpbHMgYWZ0ZXIuCgpgYGB7cn0KI0NyZWF0ZSBhIG5ldyBTdGFyYnVja3MgZGF0YXNldCB0aGF0IAojIC0gZmlsdGVycyB0byB0aGUgVVMKIyAtIHN1bW1hcml6ZXMgdGhlIG51bWJlciBvZiBTdGFyYnVja3MgaW4gZWFjaCBzdGF0ZQojIC0gaGFzIGZ1bGwgbmFtZXMgb2Ygc3RhdGVzIGluIGxvd2VyY2FzZSBsZXR0ZXJzICh0byBtYXRjaCB0byBzdGF0ZXNfbWFwIGRhdGEgY3JlYXRlZCBuZXh0KQoKc3RhcmJ1Y2tzX3VzX2J5X3N0YXRlIDwtIFN0YXJidWNrcyAlPiUgCiAgZmlsdGVyKENvdW50cnkgPT0gIlVTIikgJT4lIAogIGNvdW50KGBTdGF0ZS9Qcm92aW5jZWApICU+JSAKICBtdXRhdGUoc3RhdGVfbmFtZSA9IHN0cl90b19sb3dlcihhYmJyMnN0YXRlKGBTdGF0ZS9Qcm92aW5jZWApKSkgCgojVVMgc3RhdGVzIG1hcCBpbmZvcm1hdGlvbiAtIGNvb3JkaW5hdGVzIHVzZWQgdG8gZHJhdyBib3JkZXJzCnN0YXRlc19tYXAgPC0gbWFwX2RhdGEoInN0YXRlIikKCiMgbWFwIHRoYXQgY29sb3JzIHN0YXRlIGJ5IG51bWJlciBvZiBTdGFyYnVja3MKc3RhcmJ1Y2tzX3VzX2J5X3N0YXRlICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9tYXAobWFwID0gc3RhdGVzX21hcCwKICAgICAgICAgICBhZXMobWFwX2lkID0gc3RhdGVfbmFtZSwKICAgICAgICAgICAgICAgZmlsbCA9IG4pKSArCiAgI1RoaXMgYXNzdXJlcyB0aGUgbWFwIGxvb2tzIGRlY2VudGx5IG5pY2U6CiAgZXhwYW5kX2xpbWl0cyh4ID0gc3RhdGVzX21hcCRsb25nLCB5ID0gc3RhdGVzX21hcCRsYXQpICsgCiAgdGhlbWVfbWFwKCkKYGBgCgpOb3csIGxldCdzIGxvb2sgbW9yZSBjbG9zZWx5IGF0IHdoYXQgZWFjaCBwaWVjZSBvZiB0aGUgY29kZSBiZWxvdyBpcyBkb2luZy4KCmBgYHtyIHN0YXJidWNrcy1jaG9yLW1hcCwgZXZhbD1GQUxTRX0Kc3RhcmJ1Y2tzX3VzX2J5X3N0YXRlICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9tYXAobWFwID0gc3RhdGVzX21hcCwKICAgICAgICAgICBhZXMobWFwX2lkID0gc3RhdGVfbmFtZSwKICAgICAgICAgICAgICAgZmlsbCA9IG4pKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gc3RhdGVzX21hcCRsb25nLCB5ID0gc3RhdGVzX21hcCRsYXQpICsgCiAgdGhlbWVfbWFwKCkKYGBgCgojIyMgQ2hvb3NlIGEgbWFwCgpUaGUgYG1hcGAgYXJndW1lbnQgdGVsbHMgUiBhdCB3aGljaCBsZXZlbCB0byBjcmVhdGUgdGhlIG1hcC4gUmVhbGx5LCBpdCB0ZWxscyBpdCBob3cgdG8gZHJhdyBhbGwgdGhlIGJvcmRlcnMgVGhpcyBpcyBhIHZlcnkgc3BlY2lhbCBkYXRhIHNldC4gQWNjb3JkaW5nIHRvIHRoZSBgZ2VvbV9tYXAoKWAgZG9jdW1lbnRhdGlvbiwgaXQgaXMgYSAiZGF0YSBmcmFtZSB0aGF0IGNvbnRhaW5zIHRoZSBtYXAgY29vcmRpbmF0ZXMgLi4uIEl0ICoqbXVzdCoqIGNvbnRhaW4gY29sdW1ucyB4IG9yIGxvbmcsIHkgb3IgbGF0LCBhbmQgcmVnaW9uIG9yIGlkLiIgV2UgYXJlIHVzaW5nIHRoZSBgbWFwX2RhdGEoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIHRoZSBtYXAgZmlsZSAoc2VlIGFib3ZlIGZvciBtb3JlIGRldGFpbDAuIFlvdSBjYW4gb3BlbiB0aGUgbWFwIGRhdGEsIGBzdGF0ZXNfbWFwYCwgYW5kIHNlZSB0aGF0IGl0IGFkaGVyZXMgdG8gdGhlIHJ1bGVzLgoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJzdGFyYnVja3MtY2hvci1tYXAiLCBldmFsID0gRkFMU0UpICU+JSAKICBmbGFpcigibWFwID0gIikKYGBgCgojIyMgQ29ubmVjdCBtYXAgaWQvcmVnaW9uIHZhcmlhYmxlIHRvIGRhdGEgYmVpbmcgcGxvdHRlZAoKVGhlIGBtYXBfaWRgIGluc2lkZSBvZiBgYWVzKClgIGlzIGEgcmVxdWlyZWQgYWVzdGhldGljIGZvciB0aGUgYGdlb21fbWFwKClgIGdlb20uIEl0IHRlbGxzIFIgd2hpY2ggdmFyaWFibGUgaXMgdGhlIHJlZ2lvbi9pZCB2YXJpYWJsZSwgaW4gdGhpcyBjYXNlIHRoZSBzdGF0ZS4gSXQgY29ubmVjdHMgdGhlIGByZWdpb25gIG9yIGBpZGAgZnJvbSB0aGUgbWFwIChgcmVnaW9uYCB2YXJpYWJsZSBpbiBgc3RhdGVzX21hcGAgZGF0YXNldCwgaW4gdGhpcyBleGFtcGxlKSB0byB0aGUgZGF0YXNldCBiZWluZyBwbG90dGVkIChgc3RhdGVfbmFtZWAgaW4gYHN0YXJidWNrc191c19ieV9zdGF0ZWAsIGluIHRoaXMgZXhhbXBsZSkuIFNvIGBzdGF0ZV9uYW1lYCBuZWVkcyB0byBoYXZlIHRoZSBzYW1lIGZvcm0gYXMgYHJlZ2lvbmAsIHdoaWNoIGlzIHdoeSB3ZSBtb2RpZmllZCB0aGUgc3RhdGUgbmFtZXMgaW4gYHN0YXJidWNrc191c19ieV9zdGF0ZWAuCgpgYGB7ciwgZWNobz1GQUxTRX0KZGVjb3JhdGVfY2h1bmsoInN0YXJidWNrcy1jaG9yLW1hcCIsIGV2YWwgPSBGQUxTRSkgJT4lIAogIGZsYWlyKCJtYXBfaWQgPSAiKQpgYGAKCgojIyMgVXNlIGBnZ3Bsb3QyYCBmZWF0dXJlcwoKV2UgdGVsbCBpdCB0byBmaWxsIGluIHRoZSBzdGF0ZXMgYnkgdGhlIHZhcmlhYmxlIGBuYCwgdGhlIG51bWJlciBvZiBTdGFyYnVja3MgaW4gZWFjaCBzdGF0ZS4gV2l0aCB0aGUgYGdlb21fbWFwKClgIGdlb20sIGl0IHdpbGwgZmlsbCBpbiB0aGUgYm9yZGVycyBvZiB0aGUgcmVnaW9ucyB3ZSBkZWZpbmVkIGluIHRoZSBgbWFwYCBhcmd1bWVudC4KCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygic3RhcmJ1Y2tzLWNob3ItbWFwIiwgZXZhbCA9IEZBTFNFKSAlPiUgCiAgZmxhaXIoImZpbGwgPSAiKQpgYGAgCgojIyMgYGV4cGFuZF9saW1pdHMoKWAKClVzZSBgZXhwYW5kX2xpbWl0cygpYCB0byBhc3N1cmUgdGhhdCB0aGUgbWFwIGNvdmVycyB0aGUgZW50aXJlIGFyZWEgaXQncyBzdXBwb3NlZCB0by4gV2UgcHV0IHRoZSBsb25naXR1ZGUgdmFyaWFibGUgZnJvbSBgc3RhdGVzX21hcGAgZm9yIHRoZSBgeGAgYXJndW1lbnQgYW5kIHRoZSBsYXRpdHVkZSB2YXJpYWJsZSBmcm9tIGBzdGF0ZXNfbWFwYCBmb3IgdGhlIGB5YCBhcmd1bWVudCB0byBhc3N1cmUgdGhlIG1hcCBzdHJldGNoZXMgYWNyb3NzIHRoZSBlbnRpcmUgcmFuZ2Ugb2YgbG9uZ2l0dWRlcyBhbmQgbGF0aXR1ZGVzIGluIHRoZSBtYXAuIFRoZXJlIG1heSBiZSBhIGJldHRlciB3YXkgdG8gZG8gdGhpcyBidXQgSSBoYXZlIHlldCB0byBmaW5kIGl0LCBhbmQgd2hlbiBJIGxlYXZlIGl0IG91dCwgSSBkb24ndCBldmVuIHNlZSB0aGUgbWFwLiAKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygic3RhcmJ1Y2tzLWNob3ItbWFwIiwgZXZhbCA9IEZBTFNFKSAlPiUgCiAgZmxhaXIoImV4cGFuZF9saW1pdHMiKQpgYGAKCiMjIyBgdGhlbWVfbWFwKClgCgpUaGlzIGlzIGEgcGVyc29uYWwgcHJlZmVyZW5jZS4gSSBsaWtlIHRoZSB3YXkgYHRoZW1lX21hcCgpYCBtYWtlcyB0aGUgbWFwIGxvb2suCgpgYGB7ciwgZWNobz1GQUxTRX0KZGVjb3JhdGVfY2h1bmsoInN0YXJidWNrcy1jaG9yLW1hcCIsIGV2YWwgPSBGQUxTRSkgJT4lIAogIGZsYWlyKCJ0aGVtZV9tYXAoKSIpCmBgYAoKIyMjIEFkZCBgZ2dwbG90MmAgbGF5ZXJzCgpZb3UgY2FuIGFkZCBhbnkgb2YgdGhlIGBnZ3Bsb3QyYCBsYXllcnMgb24gdG9wIG9mIHRoaXMgbWFwLiBJbiB0aGlzIGV4YW1wbGUsIEkndmUgYWRkZWQgdGhlIE1OIFN0YXJidWNrcyBhcyBwb2ludHMsIGEgdGl0bGUsIGFuZCBjaGFuZ2VkIHRoZSBsZWdlbmQgYmFja2dyb3VuZCAoc28gaXQgZG9lc24ndCBoYXZlIG9uZSBhbmQgb3ZlcmxhcCBDYWxpZm9ybmlhKS4KCmBgYHtyfQpzdGFyYnVja3NfdXNfYnlfc3RhdGUgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX21hcChtYXAgPSBzdGF0ZXNfbWFwLAogICAgICAgICAgIGFlcyhtYXBfaWQgPSBzdGF0ZV9uYW1lLAogICAgICAgICAgICAgICBmaWxsID0gbikpICsKICBnZW9tX3BvaW50KGRhdGEgPSBTdGFyYnVja3MgJT4lIGZpbHRlcihgU3RhdGUvUHJvdmluY2VgID09ICJNTiIpLAogICAgICAgICAgICAgYWVzKHggPSBMb25naXR1ZGUsIHkgPSBMYXRpdHVkZSksCiAgICAgICAgICAgICBzaXplID0gLjA1LAogICAgICAgICAgICAgYWxwaGEgPSAuMiwgCiAgICAgICAgICAgICBjb2xvciA9ICJnb2xkZW5yb2QiKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gc3RhdGVzX21hcCRsb25nLCB5ID0gc3RhdGVzX21hcCRsYXQpICsgCiAgbGFicyh0aXRsZSA9ICJTdGFyYnVja3MgaW4gTU4iKSArCiAgdGhlbWVfbWFwKCkgKwogIHRoZW1lKGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIyBEZW1vIHZpZGVvCgpBbmQgeW91IGFyZSByZWFkeSB0byB3YXRjaCBhbm90aGVyIGRlbW8gdmlkZW8hCgo8aWZyYW1lIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL2lTNTlhNXdEckVNIiBmcmFtZWJvcmRlcj0iMCIgYWxsb3c9ImFjY2VsZXJvbWV0ZXI7IGF1dG9wbGF5OyBjbGlwYm9hcmQtd3JpdGU7IGVuY3J5cHRlZC1tZWRpYTsgZ3lyb3Njb3BlOyBwaWN0dXJlLWluLXBpY3R1cmUiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4KCltWb2ljZXRocmVhZDogYGdlb21fbWFwKClgIGRlbW9dKGh0dHBzOi8vdm9pY2V0aHJlYWQuY29tL3NoYXJlLzE1NDg0Nzk0LykKCmBgYHtyLCBlY2hvPUZBTFNFfQpkb3dubG9hZF9maWxlKAogIHBhdGggPSAiMDRfZ2VvbV9tYXBfZGVtb19ub19jb2RlLlJtZCIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGdlb21fbWFwKCkgZGVtbyBmaWxlICh3aXRob3V0IGNvZGUpIiwKICBidXR0b25fdHlwZSA9ICJ3YXJuaW5nIiwKICBoYXNfaWNvbiA9IFRSVUUsCiAgaWNvbiA9ICJmYSBmYS1zYXZlIiwKICBzZWxmX2NvbnRhaW5lZCA9IEZBTFNFCikKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KZG93bmxvYWRfZmlsZSgKICBwYXRoID0gIjA0X2dlb21fbWFwX2RlbW8uUm1kIiwKICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgZ2VvbV9tYXAoKSBkZW1vIGZpbGUgKHdpdGggY29kZSkiLAogIGJ1dHRvbl90eXBlID0gImluZm8iLAogIGhhc19pY29uID0gVFJVRSwKICBpY29uID0gImZhIGZhLXNhdmUiLAogIHNlbGZfY29udGFpbmVkID0gRkFMU0UKKQpgYGAKCgojIyMgUmVzb3VyY2VzCgoqIFtnZ3Bsb3QyIGRvY3VtZW50YXRpb25dKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZW9tX21hcC5odG1sKSAgCiogW0V4YW1wbGVdKGh0dHBzOi8vcnN0dWRpby1wdWJzLXN0YXRpYy5zMy5hbWF6b25hd3MuY29tLzc4MTQ4XzZkZDQ5YjVkYWI0YzRmNWE4YjFhNzRlNTg5M2ZmMTdkLmh0bWwpIGJ5IEFyaWUgVm9vcm1hbiAoc29tZSB0aGluZ3MgY291bGQgYmUgb3V0IG9mIGRhdGUgc2luY2UgaXQncyBmcm9tIDIwMTUpCgojIyMgWW91ciB0dXJuIQoKIyMjIyBFeGVyY2lzZTogRXZlbiBtb3JlIHdpdGggU3RhcmJ1Y2tzCgpUaGUgZXhhbXBsZSBJIHNob3dlZCBkaWQgbm90IGFjY291bnQgZm9yIHBvcHVsYXRpb24gb2YgZWFjaCBzdGF0ZSBpbiB0aGUgbWFwLiBJbiB0aGUgY29kZSBiZWxvdywgYSBuZXcgdmFyaWFibGUgaXMgY3JlYXRlZCwgYHN0YXJidWNrc19wZXJfMTAwMDBgLCB0aGF0IGdpdmVzIHRoZSBudW1iZXIgb2YgU3RhcmJ1Y2tzIHBlciAxMCwwMDAgcGVvcGxlLiBJdCBpcyBpbiB0aGUgYHN0YXJidWNrc193aXRoXzIwMThfcG9wX2VzdGAgZGF0YXNldC4KCmBgYHtyfQpjZW5zdXNfcG9wX2VzdF8yMDE4IDwtIHJlYWRfY3N2KCJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zLzZ0eHd2M2I0bmc3cGVwZS91c19jZW5zdXNfMjAxOF9zdGF0ZV9wb3BfZXN0LmNzdj9kbD0xIikgJT4lIAogIHNlcGFyYXRlKHN0YXRlLCBpbnRvID0gYygiZG90Iiwic3RhdGUiKSwgZXh0cmEgPSAibWVyZ2UiKSAlPiUgCiAgc2VsZWN0KC1kb3QpICU+JSAKICBtdXRhdGUoc3RhdGUgPSBzdHJfdG9fbG93ZXIoc3RhdGUpKQoKc3RhcmJ1Y2tzX3dpdGhfMjAxOF9wb3BfZXN0IDwtCiAgc3RhcmJ1Y2tzX3VzX2J5X3N0YXRlICU+JSAKICBsZWZ0X2pvaW4oY2Vuc3VzX3BvcF9lc3RfMjAxOCwKICAgICAgICAgICAgYnkgPSBjKCJzdGF0ZV9uYW1lIiA9ICJzdGF0ZSIpKSAlPiUgCiAgbXV0YXRlKHN0YXJidWNrc19wZXJfMTAwMDAgPSAobi9lc3RfcG9wXzIwMTgpKjEwMDAwKQpgYGAKCmEuICoqYGRwbHlyYCByZXZpZXcqKjogTG9vayB0aHJvdWdoIHRoZSBjb2RlIGFib3ZlIGFuZCBkZXNjcmliZSB3aGF0IGVhY2ggbGluZSBvZiBjb2RlIGRvZXMuCgpiLiBDcmVhdGUgYSBjaG9yb3BsZXRoIG1hcCB0aGF0IHNob3dzIHRoZSBudW1iZXIgb2YgU3RhcmJ1Y2tzIHBlciAxMCwwMDAgcGVvcGxlIG9uIGEgbWFwIG9mIHRoZSBVUy4gVXNlIGEgbmV3IGZpbGwgY29sb3IsIGFkZCBwb2ludHMgZm9yIGFsbCBTdGFyYnVja3MgaW4gdGhlIFVTIChleGNlcHQgSGF3YWlpIGFuZCBBbGFza2EpLCBhZGQgYW4gaW5mb3JtYXRpdmUgdGl0bGUgZm9yIHRoZSBwbG90LCBhbmQgaW5jbHVkZSBhIGNhcHRpb24gdGhhdCBzYXlzIHdobyBjcmVhdGVkIHRoZSBwbG90ICh5b3UhKS4gTWFrZSBhIGNvbmNsdXNpb24gYWJvdXQgd2hhdCB5b3Ugb2JzZXJ2ZS4KCgojIyBVc2luZyBgbGVhZmxldGAgdG8gY3JlYXRlIG1hcHMKCiMjIyBDb25jZXB0IE1hcAoKIVtdKC4uLy4uL2ltYWdlcy9sZWFmbGV0X2NvbmNlcHRfbWFwLnBuZykKCltMZWFmbGV0XShodHRwczovL2xlYWZsZXRqcy5jb20vKSBpcyBhbiBvcGVuLXNvdXJjZSBKYXZhU2NyaXB0IGxpYnJhcnkgZm9yIGNyZWF0aW5nIG1hcHMuIEl0IGNhbiBiZSB1c2VkIG91dHNpZGUgb2YgUiwgYnV0IHdlIHdpbGwgb25seSBkaXNjdXNzIHVzaW5nIHRoZSBgbGVhZmxldGAgbGlicmFyeSBpbiBSLiAKClRoaXMgbGlicmFyeSB1c2VzIGEgZGlmZmVyZW50IHBsb3R0aW5nIGZyYW1ld29yayBmcm9tIGBnZ3Bsb3QyYCBhbHRob3VnaCBpdCBzdGlsbCBoYXMgYSBgdGlkeXZlcnNlYCBmZWVsIGR1ZSB0byBpdHMgdXNlIG9mIHRoZSBwaXBlLCBgJT4lYCBhbmQgdGhlIHdheSBpdCBhZGRzIGxheWVycyB0byB0aGUgcGxvdCwganVzdCBsaWtlIGluIGBnZ3Bsb3QyYC4KCiMjIyBJbnRyb2R1Y3RvcnkgdmlkZW8KCldhdGNoIHRoZSB2aWRlbyB0aGF0IGludHJvZHVjZXMgYGxlYWZsZXQoKWAgZnVuY3Rpb25zLiAKCjxpZnJhbWUgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvdzVVNjJ3VWtpM0UiIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYWNjZWxlcm9tZXRlcjsgYXV0b3BsYXk7IGNsaXBib2FyZC13cml0ZTsgZW5jcnlwdGVkLW1lZGlhOyBneXJvc2NvcGU7IHBpY3R1cmUtaW4tcGljdHVyZSIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPgoKW1ZvaWNldGhyZWFkOiBNYXBwaW5nIGluIFIgd2l0aCBsZWFmbGV0XShodHRwczovL3ZvaWNldGhyZWFkLmNvbS9zaGFyZS8xNTUyNjYzNC8pCgpgYGB7ciwgZWNobz1GQUxTRX0KZG93bmxvYWRfZmlsZSgKICBwYXRoID0gIjA0X2xlYWZsZXRfaW50cm8uaHRtbCIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIHNsaWRlcyIsCiAgYnV0dG9uX3R5cGUgPSAiaW5mbyIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYAoKIyMjIyBTdGVwcyB0byBjcmVhdGUgYSBtYXAKCjEuIENyZWF0ZSBhIG1hcCB3aWRnZXQgYnkgY2FsbGluZyBgbGVhZmxldCgpYCBhbmQgdGVsbGluZyBpdCB0aGUgZGF0YSB0byB1c2UuICAKMi4gQWRkIGEgYmFzZSBtYXAgdXNpbmcgYGFkZFRpbGVzKClgICh0aGUgZGVmYXVsdCkgb3IgYGFkZFByb3ZpZGVyVGlsZXMoKWAuCjMuIEFkZCBsYXllcnMgdG8gdGhlIG1hcCBieSB1c2luZyBsYXllciBmdW5jdGlvbnMgKGUuZy4gLCBgYWRkTWFya2VycygpYCwgYGFkZFBvbHlnb25zKClgKSB0byBtb2RpZnkgdGhlIG1hcCB3aWRnZXQuICAgCjQuIFJlcGVhdCBzdGVwIDMgYXMgZGVzaXJlZC4gIAo1LiBQcmludCB0aGUgbWFwIHdpZGdldCB0byBkaXNwbGF5IGl0LgoKIyMjIyBDcmVhdGluZyBhIG1hcCB3aXRoIG1hcmtlcnMvcG9pbnRzCgpCZWxvdywgSSBjcmVhdGUgYSBiYXNpYyBtYXAgYW5kIGFkZCBteSBwb2ludHMgKHRoZSBwb2ludHMgYXJlIGEgbGF5ZXIgb24gdGhlIG1hcCkuIFRoZSBkYXRhIGFyZSBpbiBgZmF2b3JpdGVfc3RwX2J5X2xpc2FgIC0gc2VlIHRoZSBkYXRhIHNlY3Rpb24gYXQgdGhlIGJlZ2lubmluZyBmb3IgbW9yZSBkZXRhaWxzLiAKClRoZSBmdW5jdGlvbiB3ZSB3aWxsIHVzZSB0byBjcmVhdGUgdGhlIG1hcHMgd2lsbCBsb29rIGZvciBjZXJ0YWluIHZhcmlhYmxlIG5hbWVzIGZvciBsYXRpdHVkZSAobGF0LCBsYXRpdHVkZSkgYW5kIGxvbmdpdHVkZSAobG5nLCBsb25nLCBvciBsb25naXR1ZGUpLiBJZiB5b3UgZG8gbm90IG5hbWUgdGhlbSBvbmUgb2YgdGhvc2UgdGhpbmdzIG9yIGlmIHRoZSBkYXRhIHlvdSBhcmUgdXNpbmcgZG9lc24ndCBuYW1lIHRoZW0gdGhhdCwgeW91IG5lZWQgdG8gY2FsbCBvdXQgdGhlIG5hbWUgZXhwbGljaXRseSAoeW91J2xsIHNlZSB0aGF0IG5leHQpLiBZb3UgY2FuIHVzZSBhICJ0d28tZmluZ2VyIHNjcm9sbCIgdG8gem9vbSBpbiBhbmQgb3V0LgoKYGBge3J9CmxlYWZsZXQoZGF0YSA9IGZhdm9yaXRlX3N0cF9ieV9saXNhKSAlPiUgI2Jhc2UgcGxvdAogIGFkZFRpbGVzKCkgJT4lICNiYXNlIG1hcCAtIGRlZmF1bHQgaXMgb3BlbnN0cmVldCBtYXAgCiAgYWRkTWFya2VycygpICNBZGRzIG1hcmtlcnMgLSBrbm93cyBsYXQgYW5kIGxvbmcgZnJvbSBuYW1lcyBpbiBkYXRhCmBgYAoKVGhlIGdyYXBoIGJlbG93IGlzIHRoZSBzYW1lIGFzIGFib3ZlLCBidXQgaW4gdGhlIGNvZGUgSSBleHBsaWNpdGx5IHRvbGQgaXQgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSwgd2hpY2ggeW91IHdvdWxkIG5lZWQgdG8gZG8gaWYgdGhvc2UgdmFyaWFibGVzIGhhZCBhIG5hbWUgbm90IHJlY29nbml6ZWQgYnkgdGhlIGZ1bmN0aW9uLCBhbmQgYWRkZWQgbGFiZWxzLiAqKldBUk5JTkc6IERPIE5PVCBGT1JHRVQgVEhFIH4gQkVGT1JFIFRIRSBWQVJJQUJMRSBOQU1FUyEhISoqIAoKYGBge3J9CmxlYWZsZXQoZGF0YSA9IGZhdm9yaXRlX3N0cF9ieV9saXNhKSAlPiUgCiAgYWRkVGlsZXMoKSAlPiUgCiAgYWRkTWFya2VycyhsbmcgPSB+bG9uZywgCiAgICAgICAgICAgICBsYXQgPSB+bGF0LCAKICAgICAgICAgICAgIGxhYmVsID0gfnBsYWNlKSAKYGBgCgpXZSBjYW4gY2hhbmdlIGp1c3QgYWJvdXQgZXZlcnl0aGluZyBhYm91dCBvdXIgbWFwLiBUaGUgcGxvdCBiZWxvdyBpcyB0aGUgc2FtZSBwbG90IGFzIGFib3ZlIHdpdGggc29tZSBhZXN0aGV0aWMgY2hhbmdlczogCgoqIFdlIGNoYW5nZWQgdGhlIGJhc2UgbWFwIHdpdGggYGFkZFByb3ZpZGVyVGlsZXMoKWAuIAoKICArIFRvIHNlZSBhbGwgYXZhaWxhYmxlIHByb3ZpZGVyIGJhc2UgbWFwcywgdHlwZSBgcHJvdmlkZXJzYCBpbiB0aGUgY29uc29sZS4gIAogIAogICsgVG8gYWNjZXNzIHRob3NlIG1hcHMsIHVzZSBgcHJvdmlkZXJzJFBST1ZJREVSTkFNRWAgaW5zaWRlIHRoZSBgYWRkUHJvdmlkZXJUaWxlcygpYCBmdW5jdGlvbiwgd2hlcmUgYFBST1ZJREVSTkFNRWAgaXMgb25lIG9mIHRob3NlIGxpc3RlZCBgcHJvdmlkZXJzYC4gV2hlbiB5b3UgdHlwZSBgcHJvdmlkZXIkYCBhIGxpc3Qgc2hvdWxkIHNob3cgdXAgdGhhdCB5b3UgY2FuIGNsaWNrIG9uLiAKCiogV2UgY2hhbmdlZCB0aGUgbWFya2VyIHR5cGUgYnkgdXNpbmcgYGFkZENpcmNsZXMoKWAgaW5zdGVhZCBvZiBgYWRkTWFya2VycygpYCAtIFNlYXJjaCBgYWRkQ29udHJvbGAgaW4gdGhlIEhlbHAgb3IgdHlwZSBgP2FkZENvbnRyb2xgIGludG8gdGhlIGNvbnNvbGUgdG8gc2VlIHdoYXQgYWxsIHRoZSBhcmd1bWVudHMgbWVhbiBhbmQgaG93IHlvdSBjYW4gY2hhbmdlIHRoZW0uCgoqIEFsbCB2YXJpYWJsZSBhcmd1bWVudHMgYXJlIHByZWNlZGVkIHdpdGggYSB0aWxkZSwgYH5gLgoKKiBUaGUgYHdlaWdodGAgYXJndW1lbnQgdGVsbHMgaXQgaG93IHRoaWNrIHRvIG1ha2UgdGhlIGxpbmVzIG9yIHBvaW50cywgcGl4ZWxzLiAKCiogVGhlIGBvcGFjaXR5YCBhcmd1bWVudCBpcyB0aGUgdHJhbnNwYXJlbmN5LCBsaWtlIHRoZSBgYWxwaGFgIGFyZ3VtZW50IGluIGBnZ3Bsb3QyYC4gCgoqIENvbG9ycyBuZWVkIHRvIGJlIGluICJoZXgiIGZvcm0uIEkgdXNlZCB0aGUgYGNvbDJoZXgoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGdwbG90YCBsaWJyYXJ5IHRvIGRvIHRoYXQgc2luY2UgSSBkb24ndCBoYXZlIGFueSBoZXggY29sb3JzIG1lbW9yaXplZC4gVGhlIGNvbG9ycyBhbHNvIG5lZWQgdG8gYmUgdmFsaWQgUiBjb2xvcnMuCgoKYGBge3J9CmxlYWZsZXQoZGF0YSA9IGZhdm9yaXRlX3N0cF9ieV9saXNhKSAlPiUgCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkU3RhbWVuLldhdGVyY29sb3IpICU+JSAKICBhZGRDaXJjbGVzKGxuZyA9IH5sb25nLCAKICAgICAgICAgICAgIGxhdCA9IH5sYXQsIAogICAgICAgICAgICAgbGFiZWwgPSB+cGxhY2UsIAogICAgICAgICAgICAgd2VpZ2h0ID0gMTAsIAogICAgICAgICAgICAgb3BhY2l0eSA9IDEsIAogICAgICAgICAgICAgY29sb3IgPSBjb2wyaGV4KCJkYXJrYmx1ZSIpKSAKYGBgCgpUaGUgbWFwIGJlbG93IGlzIGFsc28gdGhlICJzYW1lIiBhcyB0aGUgb25lcyBJIGhhdmUgYWxyZWFkeSBjcmVhdGVkIHdpdGggYSBuZXcgYmFzZSBtYXAgYW5kIGEgbGluZSB0byB0cmFjZSBteSByb3V0ZSwgd2hpY2ggd2FzIGNyZWF0ZWQgd2l0aCB0aGUgYGFkZFBvbHlsaW5lcygpYCBsYXllci4gSXQgdHJhY2VzIHRoZSBsb2NhdGlvbnMgaW4gdGhlIG9yZGVyIHRoZXkgYXJlIGVudGVyZWQgaW4gdGhlIGRhdGFzZXQuCgpgYGB7cn0KbGVhZmxldChkYXRhID0gZmF2b3JpdGVfc3RwX2J5X2xpc2EpICU+JSAKICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRDYXJ0b0RCLkRhcmtNYXR0ZXIpICU+JSAKICBhZGRDaXJjbGVzKGxuZyA9IH5sb25nLCAKICAgICAgICAgICAgIGxhdCA9IH5sYXQsIAogICAgICAgICAgICAgbGFiZWwgPSB+cGxhY2UsIAogICAgICAgICAgICAgd2VpZ2h0ID0gMTAsIAogICAgICAgICAgICAgb3BhY2l0eSA9IDEsIAogICAgICAgICAgICAgY29sb3IgPSBjb2wyaGV4KCJkYXJrcmVkIikpICU+JSAKICBhZGRQb2x5bGluZXMobG5nID0gfmxvbmcsIAogICAgICAgICAgICAgICBsYXQgPSB+bGF0LCAKICAgICAgICAgICAgICAgY29sb3IgPSBjb2wyaGV4KCJkYXJrcmVkIikpCmBgYAoKIyMjIyBDaG9yb3BsZXRoIGxheWVycyB3aXRoIGBhZGRQb2x5Z29ucygpYAoKSW4gdGhlIGRhdGEgY29kZSBjaHVuayBhdCB0aGUgYmVnaW5uaW5nLCB0aGUgZnVuY3Rpb24gYHN0X3JlYWQoKWAgZG93bmxvYWRzIHRoZSBzaGFwZSBmaWxlIGZvciB0aGUgY291bnRpZXMgb2YgTm9ydGggQ2Fyb2xpbmEsIHdoaWNoIGlzIGluY2x1ZGVkIGluIHRoZSBgc2ZgIHBhY2thZ2UuIApUaGUgZGF0YXNldCBoYXMgbnVtYmVyIG9mIGJpcnRocyBhbmQgbnVtYmVyIG9mIFNJRFMgY2FzZXMgaW4gZWFjaCBjb3VudHkgb2YgTm9ydGggQ2Fyb2xpbmEgZnJvbSAxOTc0LTE5NzkgYW5kIDE5NzktMTk4NC4gSSBjb21wdXRlZCBhIHZhcmlhYmxlIGNhbGxlZCBgc2lkX3Blcl8xMDAwYmlydGhfNzlgIHdoaWNoIGlzIHRoZSBudW1iZXIgb2Ygc2lkcyBjYXNlcyBwZXIgMTAwMCBiaXJ0aHMgaW4gMTk3OS4gCgpCZWxvdywgSSBwcmludGVkIG91dCB0aGUgZmlyc3QgNSByb3dzIG9mIHRoZSBkYXRhIHdpdGggdGhlIE5BTUVTIGFuZCBnZW9tZXRyeSB2YXJpYWJsZXMgZnJvbSB0aGUgZGF0YXNldC4gVGhlIGBnZW9tZXRyeWAgdmFyaWFibGUgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgaG93IHRvIHBsb3QgdGhlIGJvdW5kYXJpZXMgb2YgdGhlIGNvdW50aWVzLiBPcGVuIHRoZSBkYXRhc2V0IGFuZCBleGFtaW5lIHRoZSBgZ2VvbWV0cnlgIHZhcmlhYmxlLiBUaGUgYGxlYWZsZXRgIGZ1bmN0aW9uIGtub3dzIHRoYXQgdGhlIGBnZW9tZXRyeWAgdmFyaWFibGUgY29udGFpbnMgdGhpcyBzcGVjaWFsIGluZm9ybWF0aW9uLgoKYGBge3J9Cm5jICU+JSAKICBzZWxlY3QoTkFNRSwgZ2VvbWV0cnkpICU+JSAKICBzbGljZSgxOjUpCmBgYAoKVG8gbGVhcm4gbW9yZSBhYm91dCB0aGUgZGF0YSB0eXBlIGBuY2AgaW50byB0aGUgSGVscCBtZW51IG9yIGA/bmNgIGludG8gdGhlIGNvbnNvbGUuIAoKVGhpcyBwbG90IHB1dHMgdGhlIG1hcCB3aXRoIE5vcnRoIENhcm9saW5hIGNvdW50eSBib3JkZXJzIG9uIHRvcCBvZiB0aGUgT3BlbiBTdHJlZXQgTWFwLiBUaGlzIG1hcCBpc24ndCB0aGF0IGludGVyZXN0aW5nIGJ5IGl0c2VsZi4gCmBgYHtyfQpsZWFmbGV0KG5jKSAlPiUgCiAgYWRkVGlsZXMoKSAlPiUgCiAgYWRkUG9seWdvbnMoKQpgYGAKCk5vdywgbGV0J3MgdXNlIHNvbWUgb2YgdGhlIGRhdGEgdG8gZW5oYW5jZSB0aGUgZ3JhcGggYnkgY3JlYXRpbmcgYSBjaG9yb3BsZXRoIG1hcCB3aGVyZSB3ZSBjb2xvciB0aGUgY291bnRpZXMgYnkgYHNpZF9wZXJfMTAwMGJpcnRoXzc5YC4gSW4gYGdncGxvdCgpYCwgd2UgY2FuIG1hcCBhIHZhcmlhYmxlIHRvIGNvbG9yIG9yIGZpbGwgaW5zaWRlIHRoZSBhZXN0aGV0aWMuIEJ1dCwgaW4gdGhlIGxlYWZsZXQgZnVuY3Rpb25zLCB3ZSBjYW4ndCBkbyB0aGlzLiBJbnN0ZWFkLCB3ZSBuZWVkIHRvIGNyZWF0ZSBhIHZhcmlhYmxlIG9mIGhleCBjb2xvciBuYW1lcyB0aGF0IHRlbGwgaXQgaG93IHRvIGNvbG9yIG9yIGZpbGwuIFRoYW5rZnVsbHksIHRoZXJlIGFyZSBmdW5jdGlvbnMgdGhhdCBoZWxwIHVzIGRvIHRoYXQhIExldCdzIHdhbGsgdGhyb3VnaCB0aGUgZGV0YWlsIG9mIGhvdyB3ZSBjcmVhdGVkIHRoZSBncmFwaCBiZWxvdy4gCgpgYGB7ciBjaG9yb30KI2NyZWF0ZXMgYSBmdW5jdGlvbiB0aGF0IGNhbiBiZSB1c2VkIHRvIG1hcCBhIHZhcmlhYmxlIHRvCiN0aGUgY29sb3IgcGFsZXR0ZSB1c2luZyB2aXJpZGlzCnBhbCA8LSBjb2xvck51bWVyaWMoInZpcmlkaXMiLCAKICAgICAgICAgICAgICAgICAgICAgZG9tYWluID0gbmMkc2lkX3Blcl8xMDAwYmlydGhfNzkpIAoKbGVhZmxldChuYykgJT4lIAogIGFkZFRpbGVzKCkgJT4lIAogIGFkZFBvbHlnb25zKAogICAgZmlsbENvbG9yID0gfnBhbChzaWRfcGVyXzEwMDBiaXJ0aF83OSksICNmaWxscyBhY2NvcmRpbmcgdG8gdGhhdCB2YXJpYWJsZQogICAgZmlsbE9wYWNpdHkgPSAwLjcpICNsaWtlIGFscGhhIGluIGdncGxvdApgYGAKClRoZSBgY29sb3JOdW1lcmljKClgIGZ1bmN0aW9uIHJldHVybnMgYSBmdW5jdGlvbiB0aGF0IG1hcHMgYSB2YXJpYWJsZSdzIHZhbHVlcyB0byBjb2xvcnMgaW4gdGhlIGdpdmVuIHBhbGV0dGUsIGluIHRoaXMgY2FzZSAidmlyaWRpcyIuIFNvLCBgcGFsKClgIGlzIGEgZnVuY3Rpb24uIFdlIGNhbiB0aGVuIHVzZSB0aGF0IGZ1bmN0aW9uIGluc2lkZSBgYWRkUG9seWdvbnMoKWAuIElmIHdlIGFwcGx5IHRoZSBmdW5jdGlvbiB0byB0aGUgYHNpZF9wZXJfMTAwMGJpcnRoXzc5YCB2YXJpYWJsZSwgaXQgcmV0dXJucyBhIHZhcmlhYmxlIG9mIGhleCBjb2xvcnMgYW5kIHRoZSB2YXJpYWJsZSBvZiBjb2xvcnMgaXMgdXNlZCB0byBmaWxsIHRoZSBjb3VudGllcy4gCgpCZWxvdyBJIHByaW50IG91dCB3aGF0IGhhcHBlbnMgd2hlbiBgcGFsKClgIGlzIGFwcGxpZWQgdG8gYHNpZF9wZXJfMTAwMGJpcnRoXzc5YC4gV2UgY2FuIHNlZSB0aGF0IGl0IHJldHVybnMgaGV4IGNvbG9ycy4gU28sIHRoZSB2YXJpYWJsZSBpbiB0aGUgYGZpbGxDb2xvcmAgYXJndW1lbnQgaW5zaWRlIGBhZGRQb2x5Z29ucygpYCBhYm92ZSwgaXMgYSB2YXJpYWJsZSBvZiBoZXggY29sb3IgbmFtZXMuCgpgYGB7cn0KaGVhZChwYWwobmMkc2lkX3Blcl8xMDAwYmlydGhfNzkpKQpgYGAKCkFnYWluLCB0aGlzIGlzIGRpZmZlcmVudCBmcm9tIGBnZ3Bsb3QoKWAsIHdoZXJlIHdlIGNvdWxkIG1hcCBhIHZhcmlhYmxlIHRvIGNvbG9yIG9yIGZpbGwgYW5kIGl0IHdvdWxkIGRvIHRoZSB0cmFuc2xhdGluZyBvZiB2YXJpYWJsZSB0byBjb2xvciBzY2FsZSBmb3IgdXMuIEluIHRoZSBgbGVhZmxldGAgZnVuY3Rpb25zLCB3ZSBoYXZlIHRvIGV4cGxpY2l0bHkgcHJvdmlkZSB0aGUgY29sb3JzIGluIGEgdmFyaWFibGUuIFRoZSBgY29sb3JOdW1lcmljKClgIGhlbHAgeW91IGRvIHRoYXQuIGBjb2xvckJpbigpYCwgYGNvbG9yUXVhbnRpbGUoKWAsIGFuZCBgY29sb3JGYWN0b3IoKWAgYXJlIG90aGVyIGZ1bmN0aW9ucyB5b3UgbWlnaHQgbmVlZCB0byB1c2UgZGVwZW5kaW5nIG9uIHRoZSB0eXBlIG9mIHZhcmlhYmxlIHlvdSBhcmUgdHJ5aW5nIHRvIGNoYW5nZSB0byBjb2xvcnMuCgpUaGVyZSBhcmUgbWFueSBjdXN0b21pemVhYmxlIG9wdGlvbnMgaW4gYGxlYWZsZXRgLCBtdWNoIGxpa2UgYGdncGxvdCgpYC4gSSBoYXZlIGluY2x1ZGVkIGEgY29tbWVudGVkIHNldCBvZiBjb2RlIGJlbG93IHRvIHBvaW50IG91dCBzb21lIHVzZWZ1bCBmdW5jdGlvbnMgYW5kIGFyZ3VtZW50cy4gCgpgYGB7cn0KI2NyZWF0ZXMgYSBmdW5jdGlvbiB0aGF0IGNhbiBiZSB1c2VkIHRvIG1hcCBhIHZhcmlhYmxlIHRvCiN0aGUgY29sb3IgcGFsZXR0ZSB1c2luZyB2aXJpZGlzCnBhbCA8LSBjb2xvck51bWVyaWMoInZpcmlkaXMiLCAKICAgICAgICAgICAgICAgICAgICAgZG9tYWluID0gbmMkc2lkX3Blcl8xMDAwYmlydGhfNzkpIAoKbGVhZmxldChuYykgJT4lIAogIGFkZFRpbGVzKCkgJT4lIAogIGFkZFBvbHlnb25zKAogICAgI3NraXBzIGRyYXdpbmcgdGhlIGJvcmRlcnM6CiAgICBzdHJva2UgPSBGQUxTRSwgCiAgICAjZmlsbHMgYWNjb3JkaW5nIHRvIHZhcmlhYmxlIG9mIGhleCBjb2xvcnM6CiAgICBmaWxsQ29sb3IgPSB+cGFsKHNpZF9wZXJfMTAwMGJpcnRoXzc5KSwgCiAgICAjY2hhbmdlcyB0cmFuc3BhcmVuY3ksIGxpa2UgYWxwaGEgaW4gZ2dwbG90CiAgICBmaWxsT3BhY2l0eSA9IDAuNywgCiAgICAjaG93IG11Y2ggdG8gc2ltcGxpZnkgdGhlIHBsb3Qgd2hlbiB6b29taW5nOgogICAgc21vb3RoRmFjdG9yID0gMC41LCAKICAgICNjaGFuZ2VzIHdoYXQgaGFwcGVucyB0byB0aGUgc2hhcGUgd2hlbiB3ZSBtb3VzZSBvdmVyIGl0CiAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKHdlaWdodCA9IDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC45LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmluZ1RvRnJvbnQgPSBGQUxTRSkpICU+JQogIGFkZENpcmNsZXMoZGF0YSA9IG5jX2NlbnRyb2lkLAogICAgICAgICAgICAgbG5nID0gfmNlbnRyX2xvbmcsIAogICAgICAgICAgICAgbGF0ID0gIH5jZW50cl9sYXQsIAogICAgICAgICAgICAgIyBsYWJlbCB0aGF0IGFwcGVhcnMgd2hlbiB5b3UgY2xpY2sgb24gdGhlIG1hcmtlciwKICAgICAgICAgICAgICMgaW4gdGhpcyBjYXNlIGNvdW50eSBuYW1lIGFuZCBzaWRfcGVyXzEwMDBiaXJ0aF83OQogICAgICAgICAgICAgIyByb3VuZGVkIHRvIHRoZSAybmQgZGVjaW1hbAogICAgICAgICAgICAgcG9wdXAgPSB+cGFzdGUoTkFNRSwiOiAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoc2lkX3Blcl8xMDAwYmlydGhfNzksIDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSIiKSwKICAgICAgICAgICAgIHJhZGl1cyA9IDIpICU+JSAKICAKICAjIEFkZCBhIGxlZ2VuZAogIGFkZExlZ2VuZChwYWwgPSBwYWwsIAogICAgICAgICAgICB2YWx1ZXMgPSB+c2lkX3Blcl8xMDAwYmlydGhfNzksIAogICAgICAgICAgICBvcGFjaXR5ID0gMC41LCAKICAgICAgICAgICAgdGl0bGUgPSBOVUxMLAogICAgICAgICAgICBwb3NpdGlvbiA9ICJib3R0b21yaWdodCIpIApgYGAKCiMjIyBEZW1vIHZpZGVvCgpDT01JTkcgU09PTiEKCiMjIyBSZXNvdXJjZXMKCiogW0RldGFpbGVkIGRvY3VtZW5hdGlvbl0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9sZWFmbGV0LykgKHdpdGggZXhhbXBsZXMpCgoqIFtDaGVhdHNoZWV0XShodHRwczovL3Vnb3Byb3RvLmdpdGh1Yi5pby91Z29fcl9kb2MvcGRmL2xlYWZsZXQtY2hlYXQtc2hlZXQucGRmKQoKKiBbUHJvdmlkZXIgbWFwIHByZXZpZXdzCl0oaHR0cDovL2xlYWZsZXQtZXh0cmFzLmdpdGh1Yi5pby9sZWFmbGV0LXByb3ZpZGVycy9wcmV2aWV3LykKCiMjIyBDb29sIG1hcHMKCltIaWtpbmcgbWFwIHdpdGggcGhvdG9zXShodHRwOi8vYXJjaGl2ZWQubWhlcm1hbnMubmV0L2hpa2luZy1ncHgtci1sZWFmbGV0Lmh0bWwpCltOWSBUaW1lcyBtYXlvcmFsIHByaW1hcmllc10oaHR0cDovL3d3dy5ueXRpbWVzLmNvbS9wcm9qZWN0cy9lbGVjdGlvbnMvMjAxMy9ueWMtcHJpbWFyeS9tYXlvci9tYXAuaHRtbCkKW2ZsaWNrcl0oaHR0cHM6Ly93d3cuZmxpY2tyLmNvbS9tYXApCltTdXBlciB6aXAgc2hpbnkgYXBwXShodHRwOi8vc2hpbnkucnN0dWRpby5jb20vZ2FsbGVyeS9zdXBlcnppcC1leGFtcGxlLmh0bWwpCgojIyMgWW91ciB0dXJuIQoKIyMjIyBFeGVyY2lzZTogQSBmZXcgb2YgeW91ciBmYXZvcml0ZSB0aGluZ3MKCkluIHRoaXMgZXhlcmNpc2UsIHlvdSBhcmUgZ29pbmcgdG8gY3JlYXRlIGEgc2luZ2xlIG1hcCBvZiBzb21lIG9mIHlvdXIgZmF2b3JpdGUgcGxhY2VzISBUaGUgZW5kIHJlc3VsdCB3aWxsIGJlIG9uZSBtYXAuIAoKYS4gQ3JlYXRlIGEgZGF0YSBzZXQgdXNpbmcgdGhlIGB0aWJibGUoKWAgZnVuY3Rpb24gdGhhdCBoYXMgMTAtMTUgcm93cyBvZiB5b3VyIGZhdm9yaXRlIHBsYWNlcy4gVGhlIGNvbHVtbnMgd2lsbCBiZSB0aGUgbmFtZSBvZiB0aGUgbG9jYXRpb24sIHRoZSBsYXRpdHVkZSwgdGhlIGxvbmdpdHVkZSwgYW5kIGEgY29sdW1uIHRoYXQgaW5kaWNhdGVzIGlmIGl0IGlzIGluIHlvdXIgdG9wIDMgZmF2b3JpdGUgbG9jYXRpb25zIG9yIG5vdC4gRm9yIGFuIGV4YW1wbGUgb2YgaG93IHRvIHVzZSBgdGliYmxlKClgLCBsb29rIGF0IHRoZSBgZmF2b3JpdGVfc3RwX2J5X2xpc2FgIEkgY3JlYXRlZCBpbiB0aGUgZGF0YSBSIGNvZGUgY2h1bmsgYXQgdGhlIGJlZ2lubmluZy4gIAoKICBiLiBDcmVhdGUgYSBtYXAgdGhhdCB1c2VzIGNpcmNsZXMgdG8gaW5kaWNhdGUgeW91ciBmYXZvcml0ZSBwbGFjZXMuIExhYmVsIHRoZW0gd2l0aCB0aGUgbmFtZSBvZiB0aGUgcGxhY2UuIENob29zZSB0aGUgYmFzZSBtYXAgeW91IGxpa2UgYmVzdC4gQ29sb3IgeW91ciAzIGZhdm9yaXRlIHBsYWNlcyBkaWZmZXJlbnRseSB0aGFuIHRoZSBvbmVzIHRoYXQgYXJlIG5vdCBpbiB5b3VyIHRvcCAzLiBBZGQgYSBsZWdlbmQgdGhhdCBleHBsYWlucyB3aGF0IHRoZSBjb2xvcnMgbWVhbi4gIAogIAogIGMuIENvbm5lY3QgYWxsIHlvdXIgbG9jYXRpb25zIHRvZ2V0aGVyIHdpdGggYSBsaW5lIGluIGEgbWVhbmluZ2Z1bCB3YXkgKHlvdSBtYXkgbmVlZCB0byBvcmRlciB0aGVtIGRpZmZlcmVudGx5IGluIHRoZSBvcmlnaW5hbCBkYXRhKS4gIAogIAogIGQuIElmIHRoZXJlIGFyZSBvdGhlciB2YXJpYWJsZXMgeW91IHdhbnQgdG8gYWRkIHRoYXQgY291bGQgZW5oYW5jZSB5b3VyIHBsb3QsIGRvIHRoYXQgbm93LiAgCiAgCg==